1. Building System and Makefile

what is a building system do?

  • 自动地进行文件的编译和链接,将各个文件变成一个可执行文件。通过构建系统,你可以直观地体会到整个项目是如何编译并链接的,各个文件之间的联系是什么。
  • 应当只完成必要的:也就是说当文件(依赖项)有修改时,重新编译这些修改过的文件并链接。可以省去全部编译所产生的时间。也就是说你不需要重新编译所有的代码,用 shell bash 就需要整个的进行编译。当项目非常大时,你可能需要数个小时进行编译,而用makefile,你只需要编译需要的
  • 同时,build system 应当有一定的规约(一种编程语言,比如用shell script)使得整个构建系统易于上手

一个简单的makefile. 前面的都是变量,直到 all:

CC=gcc # complier variable
INCDIRS=-I # 
OPT=-O0 # optimization variable
CFLAGS=-Wall -Wextra -g $(INCDIRS) $(OPT)

CFLAGS=x.c y.c
OBJECTS=x.o y.o

BINARY=bin

all: $(BINARY)

$(BINARY): $(OBJECTS)
		$(CC) -o $@ $^
%.o:%.c # make feature, % is a wildcard (repersenting everything)
		$(CC) $(CFLAGS) -c -o $@ $^
clean:
		rm -rf $(BINARY) $(OBJECTS)

build rule: all: $(BINARY) 就是一个build rule(这里的target和dependencies有何联系?)
all directive, 当你用make all就会转到makefile中的 (默认情况下只用 make 会转到第一个 rule)

上面的makefile的意思就是说当你在终端 make all 的时候,你就会转到 $(BINARY): $(OBJECTS)。但是BINARY depends on OBJECTS,也就是你要构建bin你就需要先得到所有的 .o 文件。如果没有,就会执行 %.o:%.c (.o 文件 依赖 .c 文件) @ 表示 LHS (.o) ^表示RHS (.c)

target: dependencies 如果所有依赖项都满足就执行 command
		command

即使你有 x.o 如果你修改了源文件,make 一个新的 x.o 将会被编译(build system的第二个事情,只做需要的事情)

需要先介绍不用variable会怎么样。

用variable之后makefile才会做需要做的事情吗?

如果头文件修改,makefile不会察觉到有什么修改了(因为.h没有依赖项

第三个例子: featureful makefile---我们将头文件都放在include文件夹中,将库放到lib文件夹中。我们将include作为依赖项,当我们修改.h文件时,makefile就能够察觉到有东西被修改了

BINARY=bin
CODEDIR=. lib
INCDIRS=. ./include/ # can be listed

CC=gcc
OPT=-O0
# generate files that encode make rules for the .h dependencies
DEPFLAGS=-MP -MD # let make work with compiler?是这样么
CFLAGS=-Wall -Wextra -g $(foreach D, $(INCDIRS),-I$(D) $(OPT) $(DEPFLAGS))

CFILES=$(foreach D,$(CODEDIRS),$(wildcard $(D)/*.c))

OBJECTS=$(patsubst %.c,%.o,%(CFILES))
DEPFILES=$(patsubst %.c,%.d,%(CFILES))

all: $(BINARY)

$(BINARY): $(OBJECT)
		$(CC) -o $@ $^

%.o:%.c # make feature, % is a wildcard (repersenting everything)
		$(CC) $(CFLAGS) -c -o $@ $< # only want .c dependency here thus $<
clean:
		rm -rf $(BINARY) $(OBJECTS) $(DEPFILES)

distribute: clean
		tar zcvf dist.tgz *

diff
# include the dependency
-include $(DEPFILES)

Pasted image 20250322033111.png

依赖项 (dependencies)

# GCC flags first
target [targets...]: [components...]
	[ command 1]
	# ...
	[ command n]

special commands

-
@
+

macros and variables

MACRO1 = 12
COMPILE = gcc *.c

gcc:
	$(COMPILE)

multi line commands
在makefile中,每行命令都默认允许在一个独立的shell终端中,也就是说在 Badlisting 中
如果需要多个命令共享同一个上下文(例如切换目录后运行操作),需要将它们合并到一个 Shell 会话中,通常使用反斜杠 \&&
分号用于在同一行中分隔多个命令。例如:

Badlisting:
	cd dir
	ls
Goodlisting:
	cd dir;\ # same as cd dir && ls
	ls

makefile 也可以用于 testing

makefile的好处就是只编译修改了的.c/.cpp文件并生成修改后的.o文件。节省编译时间。一个标准的makefile可能是这样的:

CC = gcc
INCDIRS = -I.
OPT = -O0
CFLAGS = -Wall -Wextra -g $(INCDIRS) $(OPT)

CFILES = 
OBJECTS = 
BINARY = 

all: $(BINARY)

clean:


random:
	date
	sl
	mkdir useless
	cd useless ;\
	cd..
all: # all 是干嘛的?

help: #这些是按照顺序执行的么

CC = GCC
all:
	$(CC) file.c -o proj
debug:
debug: CC += -g -DDEBUG

Where Do We Get Started?

在构建系列的

头文件:存放声明和内联化的函数/变量(constexpr/consteval...)declarations

因为内联后的符号没有定义

These files contain declarations and inline functions/variables. For example, constexpr and consteval declarations are often found in header files. Declarations inform the compiler about the existence and type of functions or variables without providing the full implementation.

Source Files (源文件): These files contain definitions. A definition is a specific type of declaration that provides complete information about an entity, including its implementation.

foo.h

int foo(int n); //A declaration, no function body.
extern int e; // A non-defining declaration.

foo.cpp

#include "foo.h"

int foo(int n) // A definition, we have the function body after this.
{
    // Function implementation goes here.
}
int a; // Definition without initialization
int b = 10; // Definition with initialization
static int c = 10; // A file-scope static definition
extern int d = 10;

other.cpp

int e = 10; // 

源文件中存放definitions, definition is a specific type of declaration. Definition gives a set of informations about the entity but no verse visa

ProjectName/
├── include/              # Header file
│   ├── main.h
│   └── utils.h
├── src/                  # Source file
│   ├── main.cpp
│   └── utils.cpp
├── lib/                  # Libraries
│   └── mylib.a
├── build/                # Object files
│   └── (object files, executables)
├── tests/                # Testing code
│   └── test_main.cpp
├── CMakeLists.txt        # CMake build file
├── Makefile              # Makefile build file
└── README.md             # Intro to the project

这节课用建立 shell 文件构建代码

gcc ....
gcc ....

gcc x.o y.o -o bin

用上面的方法的劣势就是每次构建都需要整个的编译,而不是选择性编译

下节课用 makefile